package com.mapforge.gui.frames.map;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.Iterator;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;

import com.mapforge.Constants;
import com.mapforge.listener.RenderEvent;
import com.mapforge.listener.RenderListener;
import com.mapforge.map.Tile;
import com.mapforge.map.TileLayer;
import com.mapforge.map.TileMap;
import com.mapforge.util.SwingUtil;

public class ResizeMapDialog extends JDialog {

	/**
	 * Default serial version ID, here to remove warnings.
	 */
	private static final long serialVersionUID = 1L;

	private TileMap tileMap;

	private SpinnerNumberModel modelWidth;
	private SpinnerNumberModel modelHeight;
	private JSpinner spnWidth;
	private JSpinner spnHeight;
	private JButton btnOK;
	private JButton btnCancel;

	List<RenderListener> renderListeners;

	public ResizeMapDialog(JFrame parent, TileMap tileMap,
			List<RenderListener> renderListeners) {
		super(parent, "Reize Map", true);
		this.renderListeners = renderListeners;
		this.tileMap = tileMap;
		this.modelWidth = new SpinnerNumberModel(1, 1,
				Constants.MAX_MAP_DIMENSION.width, 1);
		this.modelHeight = new SpinnerNumberModel(1, 1,
				Constants.MAX_MAP_DIMENSION.height, 1);
		this.spnWidth = new JSpinner(modelWidth);
		this.spnHeight = new JSpinner(modelHeight);
		if (tileMap != null) {
			modelWidth.setValue(tileMap.getTotalHorizontalTiles());
			modelHeight.setValue(tileMap.getTotalVerticalTiles());
		} else {
			spnWidth.setEnabled(false);
			spnHeight.setEnabled(false);
		}
		ResizeMapActionListener listener = new ResizeMapActionListener();
		this.addComponentListener(listener);
		this.btnOK = SwingUtil.createButton("OK", listener);
		this.btnCancel = SwingUtil.createButton("Cancel", listener);
		String msg = "Enter map dimensions.";
		JLabel lbl = new JLabel(msg, JLabel.CENTER);

		Box vertBox = Box.createVerticalBox();
		Box vertBox2 = Box.createVerticalBox();
		vertBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
		vertBox2.setBorder(BorderFactory.createTitledBorder(BorderFactory
				.createLineBorder(Color.BLACK), "Resize"));
		JPanel lblPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 2, 2));
		lblPanel.add(lbl);
		vertBox2.add(lblPanel);
		FlowLayout flow = new FlowLayout(FlowLayout.LEFT, 2, 2);
		vertBox2.add(SwingUtil.wrapFieldInPanel("Width:", spnWidth, flow));
		vertBox2.add(SwingUtil.wrapFieldInPanel("Height:", spnHeight, flow));
		vertBox.add(vertBox2);
		JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 2, 2));
		btnPanel.add(btnOK);
		btnPanel.add(btnCancel);
		vertBox.add(btnPanel);
		this.add(vertBox);
		this.getInsets().set(5, 5, 5, 5);
		this.pack();
		this.setResizable(false);
		this.setLocationRelativeTo(parent);
	}

	public void fireRenderEvent() {
		RenderEvent event = new RenderEvent(this);
		for (int i = 0; i < renderListeners.size(); i++) {
			renderListeners.get(i).renderEventOccurred(event);
		}
	}

	public void setTileMap(TileMap tileMap) {
		modelWidth.setValue(tileMap.getTotalHorizontalTiles());
		modelHeight.setValue(tileMap.getTotalVerticalTiles());
		spnWidth.setEnabled(true);
		spnHeight.setEnabled(true);
		this.tileMap = tileMap;
	}

	@Override
	public void setVisible(boolean value) {
		if (value) {
			((JSpinner.DefaultEditor) spnWidth.getEditor()).getTextField()
					.requestFocus();
		}
		super.setVisible(value);
	}

	private class ResizeMapActionListener implements ActionListener,
			ComponentListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			Object src = e.getSource();
			if (btnOK == src) {
				int newW = modelWidth.getNumber().intValue();
				int newH = modelHeight.getNumber().intValue();
				int oldW = tileMap.getTotalHorizontalTiles();
				int oldH = tileMap.getTotalVerticalTiles();

				if (newW != oldW || newH != oldH) {
					int width = Math.min(newW, oldW);
					int height = Math.min(newH, oldH);
					Iterator<TileLayer> it = tileMap.getTileLayerIterator();
					TileLayer layer;
					Tile[][] data;
					Tile emptyTile = tileMap.getTileset().getEmptyTile();
					// TODO create resize edit and inform interested listeners
					while (it.hasNext()) {
						layer = it.next();
						data = layer.getData();
						Tile[][] newData = new Tile[newH][newW];
						// copy valid area
						for (int y = 0; y < height; y++) {
							for (int x = 0; x < width; x++) {
								newData[y][x] = data[y][x];
							}
						}
						// fill any new area with empty tiles
						if (oldW < newW) {
							for (int y = 0; y < newH; y++) {
								for (int x = oldW; x < newW; x++) {
									newData[y][x] = emptyTile;
								}
							}
						}
						if (oldH < newH) {
							for (int y = oldH; y < newH; y++) {
								// don't copy indices that are already filled
								for (int x = 0; x < width; x++) {
									newData[y][x] = emptyTile;
								}
							}
						}
						layer.setData(newData);
					}
				}
				tileMap.setTotalHorizontalTiles(newW);
				tileMap.setTotalVerticalTiles(newH);
				fireRenderEvent();
				setVisible(false);
			} else if (btnCancel == src) {
				modelWidth.setValue(tileMap.getTotalHorizontalTiles());
				modelHeight.setValue(tileMap.getTotalVerticalTiles());
				setVisible(false);
			}
		}

		@Override
		public void componentHidden(ComponentEvent arg0) {
			modelWidth.setValue(tileMap.getTotalHorizontalTiles());
			modelHeight.setValue(tileMap.getTotalVerticalTiles());
		}

		@Override
		public void componentMoved(ComponentEvent arg0) {
			// intentionally left blank
		}

		@Override
		public void componentResized(ComponentEvent arg0) {
			// intentionally left blank
		}

		@Override
		public void componentShown(ComponentEvent arg0) {
			// intentionally left blank
		}
	}
}